/*
 * GeoVISTA Center (Penn State, Dept. of Geography)
 * Copyright (c), 2003-2011,  Jin Chen and Alan M. MacEachren, GeoVISTA Center, Penn State University
 * Licensed under Simplified BSD License
 *
 *  
   
 * @Original Author: jin Chen
 * @date: May 29, 2005$
 * @version: 1.0
 */
package edu.psu.geovista.common.utils;

import edu.psu.geovista.common.utils.collection.CollectionUtils;
import epoviz.trex.common.math.JMathUtils;
import org.apache.commons.collections.ListUtils;

import java.awt.*;
import java.util.*;
import java.util.List;

public class ArrayUtils {
    /*******************************************************************************************************
     *                string[]
     *******************************************************************************************************/
    public static boolean contain(Object[] array, Object  key){
        //org.apache.commons.lang.ArrayUtils.
        if(indexOf(array,key)>=0)
            return true;
        else
            return false;
    }
    public static int indexOf(Object[] array,Object key){
        for (int i=0;i<array.length ;i++){
            if(array[i].equals(key) )return i;
        }
        return -1;
    }
     /**
     * 
     * @param v1     e.g. row names of data file 
     * @param v2    e.g. row names of shp file
     * @return           
     */                                                                          
     public static Vector<Integer> getStringIndexMapping(String[] v1,String[] v2) {
         //Map indexs=new HashMap(names1.length );
        Vector indexs=new Vector(v1.length );
         for (int i=0;i< v1.length ;i++){
             String name = v1[i];
             boolean found=false;
             for (int j=0;j< v2.length ;j++){
                 if(v2[j].equals(name)){
                     indexs.add(j);
                     found=true;
                     break;
                 }
             }
             if(!found){
                 indexs.add(-1); 
             }
             
         }
        return indexs;
     }
    /*******************************************************************************************************
     *                 numeric
     *******************************************************************************************************/
    /**
     * delete <index>th element from given array
     * @param array
     * @param index
     * @return  new array
     */
    public static double[]  remove(double[] array,int index ){
        RangeCheck(array,index);
        if (array.length <=1){
            return new double[0];
        }

        double[] newarray=new double[array.length -1];


        System.arraycopy(array,0,newarray,0,index);
        System.arraycopy(array,index+1,newarray,index,array.length-index-1);
        return newarray;


    }

    public static int[] add(int[] a1, int[] a2){
        int len1 = a1.length;
        int len2 = a2.length;
        int[] result=new int[len1+len2];
        System.arraycopy(a1,0,result,0,len1);
        System.arraycopy(a2,0,result,len1 ,len2);
        return result;
    }
    public static double[] add(double[] a1, double[] a2){
           int len1 = a1.length;
           int len2 = a2.length;
           double[] result=new double[len1+len2];
           System.arraycopy(a1,0,result,0,len1);
           System.arraycopy(a2,0,result,len1 ,len2);
           return result;
       }

      public static Object[] add(Object[] a1, Object[] a2){
        int len1 = a1.length;
        int len2 = a2.length;
        Object[] result=new Object[len1+len2];
        System.arraycopy(a1,0,result,0,len1);
        System.arraycopy(a2,0,result,len1 ,len2);
        return result;
    }
     private static void RangeCheck(double[] array,int index) {
        if (index >= array.length )
            throw new IndexOutOfBoundsException(
            "Index: "+index+", Size: "+array.length);
     }
     /**
      * Find smallest value in the array
      * @param data
      * @return     1st = index ; 2nd=value
      */
     public static double[] findSmallest(double[] data){
        int index=findAValid(data);
         if(index<0){ //all data are NaN
             return new double[]{0,Float.NaN};
         }
         double small=data[index];

         for (int i=0;i<data.length ;i++){
              if(Double.isNaN(data[i]))continue;
             if(data[i]<small){
                small=data[i];
                index=i;
             }
         }
         return new double[]{index,small};
     }

      /**
      * Find largest value in the array
      * @param data
      * @return     1st = index ; 2nd=value
      */
     public static double[] findLargest(double[] data){
         int index=findAValid(data);
         if(index<0){ //all data are NaN
             return new double[]{0,Double.NaN};
         }
         double large=data[index];

         for (int i=0;i<data.length ;i++){
             if(Double.isNaN(data[i]))continue;

             if(data[i]>large){

                large=data[i];
                index=i;
             }
         }
         return new double[]{index,large};
     }
      /**
      * Find largest value in the array, but ignore the value with index=<ignore>
      * @param data
      * @param ignore
      * @return     1st = index ; 2nd=value
      */
     public static double[] findLargest(double[] data, int ignore){
         int index=findAValid(data);
         if(index<0){ //all data are NaN
             return new double[]{0,Float.NaN};
         }
         double large=data[index];

         for (int i=0;i<data.length ;i++){
              if(Double.isNaN(data[i]))continue;
             if(i!=ignore&&data[i]>large){
                large=data[i];
                index=i;
             }
         }
         return new double[]{index,large};
     }
    /**
     * find the first valid data
     * @param data
     * @return
     */
    public static int findAValid(double[] data){
        for (int i=0;i<data.length ;i++){
           if(!Double.isNaN(data[i]))return i;
        }
        return -1;
    }

    public static double[] todouble(float[] data){
        double[] darray=new double[data.length ];
        for (int i=0;i<data.length ;i++){
            darray[i]=data[i];
        }
        //System.arraycopy(data,0,darray,0,data.length ); not work
        return darray;
    }
      /**
     * Sort data in ascending order, return sorted index.
     * Given an array data, assume the original index of element in the array is the id.
     * Then sort array based on the value of the element, return a array with the original ids in the same order as
     * that of the value in the sorted array
     * @param data
     * @return     ascendingly-sorted index. The sort is based on data values
     */
    public static int[] getIndexOfSortedValue(float[] data){
        if(data==null){
           new NullPointerException( ArrayUtils.class.getName() +".getIDsOfSortedValue() is passed a null argument");
        }
        DataWithID[] dwi=new DataWithID[data.length ];

        for (int i = 0; i < data.length; i++){
          dwi[i]=new DataWithID();
          dwi[i].id = i;
          dwi[i].value = data[i];
        }
        Arrays.sort(dwi);
        int[] ids=new int[dwi.length ];
        for (int i=0;i<ids.length ;i++){
            ids[i]=dwi[i].id ;
        }
        return ids;
    }
    /**
     * sort selected index in the same way as getIndexOfSortedValue(float[] values)
     * @param data
     * @param indexs  index of data selected from <values>
     * @return    ascendingly-sorted index of selected data. The sort is based on data values.
     */
    public static int[] getIndexOfSortedValue(float[] data, int[] indexs){
        for (int i=0;i<indexs.length ;i++){
            if(indexs[i]<0||indexs[i]>=data.length ){
               throw new IllegalArgumentException(ArrayUtils.class.getName() +"getIndexOfSortedValue() is passed invalid data");
            }
        }

        if(data==null){
           new NullPointerException( ArrayUtils.class.getName() +".getIDsOfSortedValue() is passed a null argument");
        }
        DataWithID[] dwi=new DataWithID[data.length ];

        for (int i = 0; i < data.length; i++){
          dwi[i]=new DataWithID();
          dwi[i].id = i;
          dwi[i].value = data[i];
        }
        Arrays.sort(dwi);
        Arrays.sort(indexs);
        int[] ids=new int[indexs.length ];
        int j=0;
        for (int i=0;i<dwi.length ;i++){
            int id=dwi[i].id ;
            if(Arrays.binarySearch(indexs,id)>=0 ){//id found in selected
                ids[j++]=id;
            }
        }
        return ids;
    }

    public static class DataWithID implements Comparable {
        int id;
        double value;

        //compared by value
        public int compareTo(Object o) {
            DataWithID e = (DataWithID) o;
            int val = 0;
            if (Double.isNaN(e.value)){
                if (Double.isNaN(this.value)){
                    return 0;
                } else {
                    return 1;
                }
            }//end if the other value is NaN

            if (Double.isNaN(this.value)){
                val = -1;//everything is bigger than NaN
            } else if (this.value < e.value) {
                val = -1;
            } else if (this.value > e.value) {
                val = 1;
            }

            return val;
        }
    }
     public static float[] tofloat(double[] data){
        float[] f =new float[data.length ];
        for (int i=0;i<data.length ;i++){
            f[i]=(float) data[i];
        }
        //System.arraycopy(data,0,darray,0,data.length ); not work
        return f;
    }

    public static String[] toString(Object[] o){
        String[] s=new String[o.length ];
        for (int i=0;i<o.length ;i++){
            s[i]=(String) o[i];
        }
        return s;
    }
    public static String[] toString(float[] o){
        String[] s=new String[o.length ];
        for (int i=0;i<o.length ;i++){
            s[i]=new Float(o[i]).toString() ;
        }
        return s;
    }
    public static String[] toString(int[] o){
        String[] s=new String[o.length ];
        for (int i=0;i<o.length ;i++){
            s[i]=new Integer(o[i]).toString() ;
        }
        return s;
    }
    public static int[] toInt(float[] o){
        int[] ints=new int[o.length ];
        for (int i=0;i<o.length ;i++){
            ints[i]=(int) o[i];
        }
        return ints;
    }

    /**
     *
     * @param rowarray
     * @param colindex
     * @return a column from a 2D row-based array
     */
    public static Object[] getColumn(Object[][] rowarray, int colindex){
        int rowcount = rowarray.length;
        int colcount = rowarray[0].length;
        Object[] acol=new Object[rowcount];
        for (int i=0;i<rowarray.length ;i++){
            Object[] arow = rowarray[i];
            acol[i]=arow[colindex];

        }
        return acol;
    }
    public static void print(Object[] a){
       // System.out.println("");
        for (int i=0;i<a.length ;i++){

            System.out.print(a[i]+", ");
        }
        //System.out.println("");
    }
    public static void print(String [] a){
        for (int i=0;i<a.length ;i++){

            System.out.print(a[i]+" ");
        }

    }
     public static void print(double [] a){
        for (int i=0;i<a.length ;i++){

            System.out.print(a[i]+" ");
        }

    }
    /*******************************************************************************************************
     *                sort
     *******************************************************************************************************/
    /**
     *  
     *  
     *  
     * @param a1
     * @param a2
     * @return
     */
   public static Object[] fuzzySort(Object[] a1, Object[] a2){
       List list=new ArrayList( );
       Arrays.sort(a2);
       for (int i=0;i<a1.length ;i++){
           if(Arrays.binarySearch(a2,a1[i])>=0){//found
                list.add(a1[i]);
           }
       }
        Vector a1list = CollectionUtils.convert2Vector(a1);
        List rest = ListUtils.subtract(a1list, list);// 
        list.addAll(rest);

        return CollectionUtils.convert2Array(list);
        


    }
    public static void sortIgnoreCase(String [] a) {
        Arrays.sort(a,new StringIngoreCaseComparator());
    }
    public static class StringIngoreCaseComparator implements Comparator {
        public int compare(Object o1, Object o2) {
            String s1=(String) o1;
            String s2=(String) o2;
            return s1.compareToIgnoreCase(s2);
        }

    }

    /**
     * convert an 2D array: make a row to a column
     * e.g.
     *      1 2 3     TO   1 4
     *      4 5 6          2 5
     *                     3 6
     *
     * @param rowArray
     * @return
     */
    public static Object[][] invertArrayMatrix(Object[][] rowArray){
        int rowcount = rowArray.length;
        int colcount = rowArray[0].length;

        Object[][] mya=new Object[colcount][rowcount];
        for (int i=0;i<colcount;i++){
            for (int j=0;j<rowcount;j++){
                mya[i][j]=rowArray[j][i];
            }
        }
        return mya;
        
    }
    public static Color[][] invertArrayMatrix(Color[][] rowArray){
        int rowcount = rowArray.length;
        int colcount = rowArray[0].length;

        Color[][] mya=new Color[colcount][rowcount];
        for (int i=0;i<colcount;i++){
            for (int j=0;j<rowcount;j++){
                mya[i][j]=rowArray[j][i];
            }
        }
        return mya;

    }
    public static void testInvertArrayMatrix(){
        Integer [][] a=new Integer[][]{new Integer[]{1,2,3},new Integer[]{4,5,6}};
        Object[][] b = ArrayUtils.invertArrayMatrix(a);
        System.out.println("b"+b);

    }
    public static void main(String[] args) {
        
        testInvertArrayMatrix();
    }

    public double getSum(double[] values){
        return JMathUtils.getSum(values);
    }

}
